#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
size_t ufile_insert0(const char* path, long offset, 
        const void* ptr, size_t size, size_t nmemb)
{
    int ret;
    int n;
    
    FILE* fp = fopen(path, "r");
    ret = fseek(fp, 0, SEEK_END);
    long total = ftell(fp);
    assert(total>=offset);
    fclose(fp);

    size_t sizew = size*nmemb;
    fp = fopen(path, "a");
    fwrite(ptr, size, nmemb, fp);
    fclose(fp);

    void* buf = um_malloc(sizew);
    assert(NULL != buf);

    long woffset = total - sizew;
    fp = fopen(path, "r+");

    while(woffset >= offset ) { 
        ret = fseek(fp, woffset, SEEK_SET);
        assert(0 == ret);

        n = fread(buf, 1, sizew, fp);
        assert(n == sizew);

        n = fwrite(buf, 1, sizew, fp);
        assert(n == sizew);

        woffset -= sizew;

    }

    if(woffset < offset) {
        size_t size_last_copy = woffset + sizew - offset;
        
        ret = fseek(fp, offset, SEEK_SET);
        assert(0 == ret);

        n = fread(buf, 1, size_last_copy, fp);
        assert(n == size_last_copy);

        ret = fseek(fp, offset+sizew, SEEK_SET);
        assert(0 == ret);

        n = fwrite(buf, 1, size_last_copy, fp);
        assert(n == size_last_copy);
    }

    ret = fseek(fp, offset, SEEK_SET);
    assert(0 == ret);

    n = fwrite(ptr, size, nmemb, fp);
    assert(n == sizew);

    um_free(buf);

    return sizew;
}


#define __min(x, y) ((x)<=(y)?(x):(y))


size_t ufile_replace(const char* path, long offset, size_t length, 
        const void* ptr, size_t size, size_t nmemb)
{
    int ret;

    size_t nread;
    size_t nwrite;

    size_t sizew = size*nmemb;

    #define LEN_PATH    1024
    char path_tmp[LEN_PATH];
    snprintf(path_tmp, LEN_PATH, "%s.tmp.%d.%lu", path, getpid(), pthread_self());
    FILE* fr = fopen(path, "r");
    assert(NULL != fr);
    FILE* fw = fopen(path_tmp, "w");
    assert(NULL != fw);

    #define LEN_BUF 1024
    void* buf = um_malloc(LEN_BUF);
    assert(NULL != buf);

    size_t size_read = 0;
    while(size_read < offset) {
        nread = fread(buf, 1, __min(offset-size_read, LEN_BUF), fr);
        assert(0 != nread);

        nwrite = fwrite(buf, 1, nread, fw);
        assert(nread == nwrite);

        size_read += nread;
    }

    if(length != 0) {
        ret = fseek(fr, length, SEEK_CUR);
        assert(0 == ret);
    }

    nwrite = fwrite(ptr, size, nmemb, fw);
    assert(nwrite == sizew);

    while(1) {
        nread = fread(buf, 1, LEN_BUF, fr);
        if(nread == 0) {
            break;
        }

        nwrite = fwrite(buf, 1, nread, fw);
        assert(nread == nwrite);
    }

    fclose(fr);
    fclose(fw);

    ret = rename(path_tmp, path);
    assert(0 == ret);

    return sizew;
}


size_t ufile_insert(const char* path, long offset, 
        const void* ptr, size_t size, size_t nmemb)
{
    return ufile_replace(path, offset, 0, ptr, size, nmemb);
}


int main()
{
    const char* s = "123456";
    ufile_insert("o.txt", 1, s, 1, 6);

    return 0;
}


#if 0
    int i;
    for(i=-10; i<=10; i++) {
        FILE* fp = fopen("o.txt", "r+");
        printf("%3d : %d\n", i, fseek(fp, i, SEEK_END));
        fclose(fp);
    }

    return 0;
#endif


